import os
import requests
import hashlib
import random
import string
import time
import xml.etree.ElementTree as ET

from dotenv import load_dotenv

# 加载同级或项目根目录下的 .env 文件
dotenv_path = os.path.join(os.path.dirname(__file__), '../../.env')  # 根据你的实际位置调整
load_dotenv(dotenv_path)


class WeixinPayV2:
    def __init__(self, tenant_config=None):
        # 如果传入了租户配置，使用租户配置；否则使用环境变量（兼容性）
        if tenant_config:
            self.appid = tenant_config.get('app_id')
            self.mchid = tenant_config.get('mch_id')
            self.api_key = tenant_config.get('api_key')
            self.cert_path = tenant_config.get('cert_path')
            self.key_path = tenant_config.get('key_path')
        else:
            # 兼容原来的环境变量方式
            self.appid = os.environ.get('MINIPROGRAM_APPID')
            self.mchid = os.environ.get('MCH_ID')
            self.api_key = os.environ.get('PAY_KEY')
            self.cert_path = os.environ.get('CERT_PATH')
            self.key_path = os.environ.get('KEY_PATH')

        self.unified_order_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"

    def _create_nonce_str(self, length=32):
        return ''.join(random.choices(string.ascii_letters + string.digits, k=length))

    def _dict_to_xml(self, params):
        xml = ["<xml>"]
        for k, v in params.items():
            xml.append(f"<{k}><![CDATA[{v}]]></{k}>")
        xml.append("</xml>")
        return ''.join(xml)

    def _xml_to_dict(self, xml_str):
        root = ET.fromstring(xml_str)
        return {child.tag: child.text for child in root}

    def _sign(self, params):
        # 排序并拼接参数
        stringA = "&".join([f"{k}={params[k]}" for k in sorted(params) if params[k]])
        stringSignTemp = f"{stringA}&key={self.api_key}"
        return hashlib.md5(stringSignTemp.encode('utf-8')).hexdigest().upper()

    def create_unified_order(self, body, out_trade_no, total_fee, spbill_create_ip, notify_url, openid):
        params = {
            "appid": self.appid,
            "mch_id": self.mchid,
            "nonce_str": self._create_nonce_str(),
            "body": body,
            "out_trade_no": out_trade_no,
            "total_fee": str(total_fee),  # 单位分
            "spbill_create_ip": spbill_create_ip,
            "notify_url": notify_url,
            "trade_type": "JSAPI",
            "openid": openid
        }
        params["sign"] = self._sign(params)
        xml = self._dict_to_xml(params)

        # 发起请求
        response = requests.post(self.unified_order_url, data=xml.encode('utf-8'), headers={"Content-Type": "text/xml"})
        response.encoding = 'utf-8'
        resp_dict = self._xml_to_dict(response.text)

        if resp_dict.get("return_code") != "SUCCESS":
            raise Exception(f"微信统一下单失败: {resp_dict.get('return_msg')}")
        if resp_dict.get("result_code") != "SUCCESS":
            raise Exception(f"微信统一下单失败: {resp_dict.get('err_code_des')}")

        return self._build_jsapi_pay_params(resp_dict["prepay_id"])

    def _build_jsapi_pay_params(self, prepay_id):
        time_stamp = str(int(time.time()))
        nonce_str = self._create_nonce_str()
        package = f"prepay_id={prepay_id}"

        pay_sign_params = {
            "appId": self.appid,
            "timeStamp": time_stamp,
            "nonceStr": nonce_str,
            "package": package,
            "signType": "MD5"
        }
        pay_sign = self._sign(pay_sign_params)

        return {
            "timeStamp": time_stamp,
            "nonceStr": nonce_str,
            "package": package,
            "signType": "MD5",
            "paySign": pay_sign
        }


class WeixinPayV3:
    def __init__(self, tenant_config=None):
        # 如果传入了租户配置，使用租户配置；否则使用环境变量（兼容性）
        if tenant_config:
            self.appid = tenant_config.get('app_id')
            self.mchid = tenant_config.get('mch_id')
            self.api_key = tenant_config.get('api_key')
            self.cert_path = tenant_config.get('cert_path')
            self.key_path = tenant_config.get('key_path')
        else:
            # 兼容原来的环境变量方式
            self.appid = os.environ.get('GMINIPROGRAM_APPID')
            self.mchid = os.environ.get('MCH_ID')
            self.api_key = os.environ.get('PAY_KEY')
            self.cert_path = os.environ.get('CERT_PATH')
            self.key_path = os.environ.get('KEY_PATH')

        self.unified_order_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"

    def _create_nonce_str(self, length=32):
        return ''.join(random.choices(string.ascii_letters + string.digits, k=length))

    def _dict_to_xml(self, params):
        xml = ["<xml>"]
        for k, v in params.items():
            xml.append(f"<{k}><![CDATA[{v}]]></{k}>")
        xml.append("</xml>")
        return ''.join(xml)

    def _xml_to_dict(self, xml_str):
        root = ET.fromstring(xml_str)
        return {child.tag: child.text for child in root}

    def _sign(self, params):
        # 排序并拼接参数
        stringA = "&".join([f"{k}={params[k]}" for k in sorted(params) if params[k]])
        stringSignTemp = f"{stringA}&key={self.api_key}"
        return hashlib.md5(stringSignTemp.encode('utf-8')).hexdigest().upper()

    def create_unified_order(self, body, out_trade_no, total_fee, spbill_create_ip, notify_url, openid):
        params = {
            "appid": self.appid,
            "mch_id": self.mchid,
            "nonce_str": self._create_nonce_str(),
            "body": body,
            "out_trade_no": out_trade_no,
            "total_fee": str(total_fee),  # 单位分
            "spbill_create_ip": spbill_create_ip,
            "notify_url": notify_url,
            "trade_type": "JSAPI",
            "openid": openid
        }
        params["sign"] = self._sign(params)
        xml = self._dict_to_xml(params)
        print(f"请求微信统一下单的XML: {params}")
        # 发起请求
        response = requests.post(self.unified_order_url, data=xml.encode('utf-8'), headers={"Content-Type": "text/xml"})
        response.encoding = 'utf-8'
        resp_dict = self._xml_to_dict(response.text)

        if resp_dict.get("return_code") != "SUCCESS":
            raise Exception(f"微信统一下单失败: {resp_dict.get('return_msg')}")
        if resp_dict.get("result_code") != "SUCCESS":
            raise Exception(f"微信统一下单失败: {resp_dict.get('err_code_des')}")

        return self._build_jsapi_pay_params(resp_dict["prepay_id"])

    def _build_jsapi_pay_params(self, prepay_id):
        time_stamp = str(int(time.time()))
        nonce_str = self._create_nonce_str()
        package = f"prepay_id={prepay_id}"

        pay_sign_params = {
            "appId": self.appid,
            "timeStamp": time_stamp,
            "nonceStr": nonce_str,
            "package": package,
            "signType": "MD5"
        }
        pay_sign = self._sign(pay_sign_params)

        return {
            "timeStamp": time_stamp,
            "nonceStr": nonce_str,
            "package": package,
            "signType": "MD5",
            "paySign": pay_sign
        }

# wxpay = WeixinPayV2()
#
# result = wxpay.create_unified_order(
#     body="购买课程",
#     out_trade_no="ORDER123456789",
#     total_fee=1,  # 单位是分，1 分 = 0.01 元
#     spbill_create_ip="127.0.0.1",
#     notify_url="https://0c25-240e-398-5a90-3380-5485-d1ab-d8a3-7586.ngrok-free.app/pay_notify",
#     openid="oFDKa7QR91KxSxxHC0EEOdj8tvt0"
# )
